using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;


namespace GameModels
{
    /// <summary>
    /// This is a game component that implements IUpdateable.
    /// </summary>
    public class ModelBase : Microsoft.Xna.Framework.DrawableGameComponent
    {
        public ModelBase(Game game)
            : base(game)
        {
            // TODO: Construct any child components here   
            this.CanMove = false;
            this.IsFocused = false;
            this.Scale = 1;
        }
        public virtual void MoveUp()
        {
            Vector3 direction = Vector3.Transform(new Vector3(0, 0, 1), Matrix.CreateFromYawPitchRoll(Yaw, Picth, Roll));
            direction.Normalize();
            Position = Position + Speed * direction;
        }
        public virtual void MoveDown()
        {
            Vector3 direction = Vector3.Transform(new Vector3(0, 0, 1), Matrix.CreateFromYawPitchRoll(Yaw, Picth, Roll));
            direction.Normalize();
            Position = Position - Speed * direction;
        }
        public virtual void MoveLeft()
        {            
            Yaw += 0.001f*Speed;
        }
        public virtual void MoveRight()
        {
            Yaw -= 0.001f*Speed;
        }
        public bool IsFocused
        {
            get;
            set;
        }
        private Effect _Effect = null;
        private string _WorldName;
        private string _ViewName;
        private string _ProjectionName;
        public void SetEffect(Effect efx, string worldName, string viewName, string projectionName)
        {
            _Effect = efx;
            _ProjectionName = projectionName;
            _WorldName = worldName;
            _ViewName = viewName;
            foreach (ModelMesh mesh in Model.Meshes)
            {
                foreach (ModelMeshPart part in mesh.MeshParts)
                {
                    part.Effect = _Effect.Clone();
                }
            }
        }
        public void SetBoneTransForm(string boneName, Matrix transform)
        {
            Model.Bones[boneName].Transform = transform;
        }
        public Matrix GetBoneTransForm(string boneName)
        {
            return Model.Bones[boneName].Transform;
        }
        public float Speed { get; set; }
        public bool CanMove { get; set; }
        public Model Model { get; set; }//Point to 3D Model
        public Vector3 Position { get; set; }//Location of model
        public Matrix View { get; set; }//Camera matrix
        public Matrix Projection { get; set; }//Projection matrix
        public Matrix World
        {
            get
            {
                return worldTranform;
            }
        }
        private Matrix worldTranform;
        private Matrix[] boneTransforms;
        public float Scale { get; set; }
        public float Yaw { get; set; }
        public float Picth { get; set; }
        public float Roll { get; set; }
        public bool IsPressed { get; set; }
        private Vector3 _ModelEye;
        public Vector3 ModelEye
        {
            get
            {
                return Scale*_ModelEye;
            }
            set
            {
                _ModelEye = value;
            }
        }
        //Method to load model object from content
        public void LoadModel(string assetName)
        {
            Model = Game.Content.Load<Model>(assetName);
            boneTransforms = new Matrix[Model.Bones.Count];
            Game.Components.Add(this);
        }
        /// <summary>
        /// Allows the game component to perform any initialization it needs to before starting
        /// to run.  This is where it can query for any required services and load content.
        /// </summary>
        public override void Initialize()
        {
            // TODO: Add your initialization code here

            base.Initialize();
        }

        /// <summary>
        /// Allows the game component to update itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Update(GameTime gameTime)
        {
            // TODO: Add your update code here
            if (CanMove)
            {
                KeyboardState keyState = Keyboard.GetState();
                IsPressed = false;
                if (keyState.IsKeyDown(Keys.Up))
                {
                    IsPressed = true;
                    MoveUp();
                }
                if (keyState.IsKeyDown(Keys.Down))
                {
                    IsPressed = true;
                    MoveDown();
                }
                if (keyState.IsKeyDown(Keys.Right)&&Speed>0)
                {
                    MoveRight();
                }
                if (keyState.IsKeyDown(Keys.Left) && Speed > 0)
                {
                    MoveLeft();
                }
            }
            worldTranform = Matrix.CreateFromYawPitchRoll(Yaw, Picth, Roll) * Matrix.CreateTranslation(Position);
            if (Scale > 0 && Scale != 1)
                worldTranform = Matrix.CreateScale(Scale) * worldTranform;

            base.Update(gameTime);
        }
        public override void Draw(GameTime gameTime)
        {
            if (Model == null) return;
            Model.CopyAbsoluteBoneTransformsTo(boneTransforms);

            foreach (ModelMesh mesh in Model.Meshes)
            {
                Matrix transformBone = boneTransforms[mesh.ParentBone.Index] * worldTranform;

                foreach (Effect effect in mesh.Effects)
                {
                    if (effect is BasicEffect)
                    {
                        BasicEffect basicEffect = (BasicEffect)effect;
                        basicEffect.EnableDefaultLighting();
                        basicEffect.View = View;
                        basicEffect.World = transformBone;
                        basicEffect.Projection = Projection;
                        
                    }
                    else
                    {
                        if (_WorldName != "")
                            effect.Parameters[_WorldName].SetValue(transformBone);
                        if (_ViewName != "")
                            effect.Parameters[_ViewName].SetValue(View);
                        if (_ProjectionName != "")
                            effect.Parameters[_ProjectionName].SetValue(Projection);
                    }
                }
                mesh.Draw();

            }
            base.Draw(gameTime);
        }
    }
}
